package org.nf.web.servlet;

import cn.hutool.core.exceptions.ExceptionUtil;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.nf.web.config.Configure;
import org.nf.web.view.DefaultView;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

/**
 * 请求总控器，负责接受所有请求，然后根据请求地址
 * 分发给不同的后端控制器（Controller）
 * 当 url-pattern 设置为 "/" 时，会覆盖默认的 DefaultServlet
 * 所有的 web 服务器都会有一个·默认的 servlet，这个 servlet
 * 专门用于处理静态资源
 *
 * @author zhu
 * @date 2024-05-14
 */
public class DispatcherServlet extends HttpServlet {

    /**
     * HandlerMapping集合，负责查找不同的 Controller
     */
    private final List<HandlerMapping> handlerMappings = new ArrayList<>();

    /**
     * HandlerInvoke集合，负责调用不同的 Controller
     */
    private final List<HandlerInvoke> handlerInvokes = new ArrayList<>();

    /**
     * 基于SPI机制，加载所有 HandlerMapping
     */
    private final ServiceLoader<HandlerMapping> mappingLoader = ServiceLoader.load(HandlerMapping.class);

    /**
     * 基于SPI机制，加载所有 HandlerInvoke
     */
    private final ServiceLoader<HandlerInvoke> invokeLoader = ServiceLoader.load(HandlerInvoke.class);

    /**
     * 异常解析器集合
     */
    private List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        ServletContext sc = config.getServletContext();
        // 从ServletContext中获取配置对象
        Configure configure = (Configure) sc.getAttribute("configure");
        // 初始化HandlerMapping
        initHandlerMapping(configure);
        // 初始化HandlerInvoke
        initHandlerInvoke();
        // 初始化异常解析器
        initExceptionResolvers(configure);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletApi servletApi = new ServletApi(req, resp);
        // 根据请求获取可以处理请求的handler
        Object handler = getHandler(servletApi.req());
        // 如果handler不为空则交给HandlerInvoker去调用
        if (handler != null) {
            // 将Controller交给HandlerInvoke负责去调用
            // 注意：此时的controller对象有可能是Class对象也有可能是一个Method对象
            // 但是DispatcherServlet是不能识别它是哪一个对象，所以不能强转为Class，
            // 必须要有一种机制去适配这些不同的controller
            // 找到合适匹配的adapter
            HandlerInvoke invoker = getInvoke(handler);
            Object viewObj = null;
            try {
                //调用handler的方法处理请求
                viewObj = invoker.invoke(handler, servletApi);
            } catch (Exception e) {
                // 处理全局异常
                viewObj = handlerException(e);
            }
            // 渲染视图
            render(viewObj,servletApi);
        }else {
            //根据Servlet的名称进行转发
            req.getServletContext().getNamedDispatcher("default").forward(servletApi.req(), servletApi.resp());
        }

    }

    /**
     * 初始化所有的 HandlerMapping
     */
    private void initHandlerMapping(Configure configure) {
        for (HandlerMapping mapping : mappingLoader) {
            //初始化
            mapping.init(configure);
            //添加到集合中
            handlerMappings.add(mapping);
        }
    }

    /**
     * 初始化所有的 HandlerInvoke
     */
    private void initHandlerInvoke() {
        for (HandlerInvoke invoke : invokeLoader) {
            handlerInvokes.add(invoke);
        }
    }

    /**
     * 初始化异常解析器
     *
     * @param configure 配置对象
     */
    private void initExceptionResolvers(Configure configure) {
        for (HandlerExceptionResolver exceptionResolver : ServiceLoader.load(HandlerExceptionResolver.class)) {
            // 初始化
            exceptionResolver.init(configure);
            // 添加到List集合中
            exceptionResolvers.add(exceptionResolver);
        }
    }

    /**
     * 根据 HandlerMapping查找合适的handler
     * 这个handler可能是Class也可能是Method对象
     *
     * @param request 请求地址
     * @return controller 对象
     */
    private Object getHandler(HttpServletRequest request){
        for(HandlerMapping mapping : handlerMappings) {
            Object handler = mapping.getHandler(request);
            if(handler != null) {
                return handler;
            }
        }
        return null;
    }

    /**
     * 根据handler找到合适的适配器
     *
     * @param controller controller 对象
     * @return invoke 对象
     */
    private HandlerInvoke getInvoke(Object controller){
        for(HandlerInvoke invoker : handlerInvokes) {
            if(invoker.supports(controller)) {
                return invoker;
            }
        }
        throw new RuntimeException("HandlerInvoker not found!");
    }

    /**
     * 异常处理
     * @param e 产生的异常对象
     * @return 视图对象
     */
    private View handlerException(Exception e){
        // 先获取真正引发异常的的对象
        Throwable t = ExceptionUtil.getRootCause(e);
        t.printStackTrace();
        View view = null;
        // 只要有一个异常解析器可以解析成功，就跳出循环
        for (HandlerExceptionResolver resolver : exceptionResolvers){
            view = resolver.resolveException(t);
            if(view != null) {
                break;
            }
        }
        return view;
    }

    /**
     * 渲染视图对象
     *
     * @param object 由适配器返回的视图对象
     */
    private void render(Object object, ServletApi servletApi) throws ServletException, IOException {

        if (object != null){
            // 如果是View接口的实例，则强转后执行渲染
            // 否则使用默认视图来处理响应
            if (object instanceof View view){
                view.render(servletApi);
            } else {
                new DefaultView(object).render(servletApi);
            }
        }
    }
}
